package controllers
{
	import models.DebuggerHistoryEntry;
	import models.Variable;
	
	import mx.collections.ArrayCollection;
	import mx.core.Application;
	import mx.rpc.events.ResultEvent;
	
	import ui.windows.DebugAddWatchWindow;
	
	import utils.BlockingHTTPService;
	import utils.MessageBus;

	public class DebuggerController extends Object
	{
		public function DebuggerController()
		{
			super();
		}
		
		public function send(command:String, listener:Function = null):void{
			var service:BlockingHTTPService = new BlockingHTTPService();
			service.url = Config.serverURL + "/debug";
			service.resultFormat = "xml";
			service.waitingTime = 4000;
			if(listener != null)
				service.addEventListener(ResultEvent.RESULT, listener);
			else
				service.addEventListener(ResultEvent.RESULT, onExecResult);
			
			var params:Object = new Object();
			params.cmd = command;
			
			service.send(params);			
		}
		
		// Select a source line
		public function selectSourceLine(lineNumber:int):void{
			Application.application.debugMainView.debugSourceView.selectedIndex = lineNumber - 1;
		}
		
		// Select the console lines printed by the debugger history entry
		public function selectConsoleLinesByHistory(entry:DebuggerHistoryEntry):void{
			var indices:Array = [];
			for(var i:uint = entry.beginIndex; i <= entry.endIndex; i++)
				indices.push(i);
			
			Application.application.debugMainView.debugConsoleView.consoleList.scrollToIndex(indices[0]);
			Application.application.debugMainView.debugConsoleView.consoleList.selectedIndices = indices;
		}
		
		// Go to the selected history entry
		public function navigateToHistory(entry:DebuggerHistoryEntry):void{
			// Update watch
			Application.application.watchController.updateDebuggerWatches(entry);
			
			// Auto select corrsponding console lines
			selectConsoleLinesByHistory(entry);
			
			// Auto select corrsponding source line
			selectSourceLine(entry.lineNumber);
		}
		
		// Start debug session
		public function start(args:String):void{
			var service:BlockingHTTPService = new BlockingHTTPService();
			service.url = Config.serverURL + "/debug";
			service.resultFormat = "xml";
			service.addEventListener(ResultEvent.RESULT, onStartResult);
			
			var params:Object = new Object();
			params.cmd = "start";
			params.source = Application.application.sourceController.source;
			params.file_name = Application.application.sourceController.sourceFile.nativePath;
			params.args = args;
			
			service.send(params);
			
			// Enter debug mode
			Application.application.viewMode.selectedIndex = 1;
		}
		
		private function onStartResult(evt:ResultEvent):void{
			onExecResult(evt);
		}	

		// End debug session
		public function end():void{
			send("end", onEndResult);
		}
		
		private function onEndResult(evt:ResultEvent):void{
			Application.application.viewMode.selectedIndex = 0;
			
			MessageBus.post("Debugger session terminated");
		}	
		
		// Next
		public function next():void{
			send("n", onExecResult);
		}
	
		// Step
		public function step():void{
			send("s", onExecResult);
		}
		
		// Continue and run
		public function run():void{
			send("r", onExecResult);
		}
				
		// Set/unset breakpoint
		public function setBreakPoint(lineNumber:Number, breakpoint:Boolean):void{
			if(breakpoint)
				send("b " + lineNumber.toString(), onExecResult);
			else
				send("clear " + lineNumber.toString(), onExecResult);
		}
		
		// Print variable (Quick Print)
		public function print(expr:String):void{
			send("p " + expr, onExecResult);
		}
		
		// Execute a command (Quick Command)
		public function exec(expr:String):void{
			send(expr, onExecResult);
		}
		
		private function onExecResult(evt:ResultEvent):void{
			var result:XML = XML(evt.result);
			var i:uint;
			
			// Extract console output
			var consoleRaw:String = String(result.console[0]);			
			var consoleLines:Array = consoleRaw.split("\n");
			
			// Add a command history entry
			var entry:DebuggerHistoryEntry = new DebuggerHistoryEntry();
			entry.lineNumber = Number(String(result.lineno[0]));
			entry.command = String(result.cmd[0]);
			entry.consoleOutput = new ArrayCollection(consoleLines);
			entry.beginIndex = Application.application.debuggerConsoleOutput.length;
			entry.endIndex = entry.beginIndex + consoleLines.length - 1;
			Application.application.historyController.addDebuggerHistoryEntry(entry);
			
			// Append output to console
			for each(var line:String in consoleLines){
				Application.application.debuggerConsoleOutput.addItem(line);
			}

			// Extract backtraces
			for(i=0;i<result.backtraces[0].children().length();i++){
				var traceObject:XML = result.backtraces[0].backtrace[i];
				var traceEntry:Object = {lineNumber: String(traceObject.@lineno), functionName: String(traceObject.@name)};
				entry.backtrace.addItem(traceEntry);
			}
				
			// Set to selected entry
			Application.application.selectedDebuggerEntry = entry;
			
			// Show the executing source line
			selectSourceLine(entry.lineNumber);
			
			// Update watches
			for(i=0;i<result.variables[0].children().length();i++){
				var variableObject:Object = result.variables[0].variable[i];
				var variable:Variable = new Variable();
				variable.name = String(variableObject.@name);
				variable.value = String(variableObject.@value);
				variable.scope = "watch"; 
				
				entry.variables.addItem(variable);
			}

			Application.application.watchController.updateDebuggerWatches(entry);
			
			// Auto navigate to this entry
			navigateToHistory(entry);
			
			// Send a notofication
			MessageBus.post("Executed command '" + entry.command + "'");
		}
		
		// Get all the history values for a certain watch variable
		public function formatHistory(watch:Variable):ArrayCollection{
			var history:ArrayCollection = new ArrayCollection();
			
			var commandID:Number = 0;
			for each(var entry:DebuggerHistoryEntry in Application.application.debuggerHistory){
				var variables:ArrayCollection = entry.variables;
				for each(var variable:Variable in variables){
					if(variable.name == watch.name)
					{
						if(!variable.value.toString().search(/[a-zA-Z]*/))
							history.addItem({commandID: commandID, value: variable.value});
					}
				}
				
				commandID++;
			}
			
			return history;
		}
		
		public function showWatchHistory(watch:Variable):void{
			Application.application.debugMainView.debugViewStack.selectedIndex = 1;
		}
		
	}
}